﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using MishraReader.Entities.Tools;

#if !WINRT

using System.Windows;
#else
using Windows.Data.Html;
using Windows.UI.Xaml;
#endif

namespace MishraReader.Entities
{
    [DataContract]
    [DebuggerDisplay("Title = {Title}")]
    public class Item : Notifier, IEquatable<Item>
    {
        private string _description;
        private string _formatedPublicationDate;
        private string _preparedDescription;
        private DateTime? _publicationDate;

        public Item()
        {
            OnDeserialized(default(StreamingContext));
        }


        [OnDeserialized]
        private void OnDeserialized(StreamingContext c)
        {
            if(_categories == null)
                _categories = new List<string>();

            if (_alternates == null)
                _alternates = new List<Alternate>();

        }

        [DataMember(Name = "alternate")]
        private List<Alternate> _alternates;
        
        public IEnumerable<Alternate> Alternates
        {
            get { return _alternates; }
        }

        [DataMember(Name = "author")]
        public string Author { get; set; }

        private bool _isReadStateLocked;

        [DataMember(Name="isReadStateLocked")]
        public bool IsReadStateLocked
        {
            get { return _isReadStateLocked; }
            set { Set(ref _isReadStateLocked, value); }
        }

        [DataMember(Name = "categories")]
        private List<string> _categories;

        
        public IEnumerable<string> Categories
        {
            get { return _categories; }
        }

        [DataMember(Name = "content")]
        public Content Content { get; set; }


        public string Description
        {
            get
            {
                EnsureDescriptionInitialized();
                return _description;
            }
        }

        public Content DisplaySummary
        {
            get { return Summary ?? Content; }
        }

        public String FormatedPublicationDate
        {
            get
            {
                if (String.IsNullOrEmpty(_formatedPublicationDate))
                {
                    if (PublicationDate.Date == DateTime.Now.Date)
                        _formatedPublicationDate = PublicationDate.ToString("t");
                    else
                    {
                        var month = PublicationDate.ToString("MMM").Replace(".", String.Empty);
                        _formatedPublicationDate = String.Format(Strings.ItemDateFormat, month, PublicationDate);
                    }
                }

                return _formatedPublicationDate;
            }
        }

        [DataMember(Name = "id")]
        public string Id { get; set; }

        public string ImageUrl { get; private set; }

        [DataMember(Name = "origin")]
        public Origin Origin { get; set; }

        public Uri PostUri
        {
            get
            {
                var correctAlternate = Alternates.FirstOrDefault(a => a.Type == "text/html");

                return new Uri(correctAlternate.Href, UriKind.Absolute);
            }
        }

        public DateTime PublicationDate
        {
            get
            {
                if (!_publicationDate.HasValue)
                {
                    if (!string.IsNullOrEmpty(TimestampUsec))
                        _publicationDate = TimestampUsec.FromUSecUnixTime();
                    else
                        _publicationDate = new DateTime(1970, 1, 1);
                }

                return _publicationDate.Value;
            }
        }

        public bool Starred
        {
            get { return Categories.FirstOrDefault(i => i.EndsWith("/state/com.google/starred")) != null; }
        }

        public Subscription Subscription { get; internal set; }

        [DataMember(Name = "summary")]
        public Content Summary { get; set; }

        [DataMember(Name = "timestampUsec")]
        public string TimestampUsec { get; set; }

        [DataMember(Name = "title")]
        public string Title { get; set; }

        public bool Unread
        {
            get { return Categories.FirstOrDefault(i => i.EndsWith("/state/com.google/read")) == null; }
        }


        public bool Equals(Item other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.Id, Id);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof(Item)) return false;
            return Equals((Item)obj);
        }

        public override int GetHashCode()
        {
            return (Id != null ? Id.GetHashCode() : 0);
        }

        public bool CanMarkAsRead()
        {
            if (Starred)
                return false;

            if (!Unread)
                return false;

            if (IsReadStateLocked)
                return false;

            return true;
        }

        public void MarkAsRead()
        {

            if (!CanMarkAsRead())
                return;
            //Debug.WriteLine("MarkAsRead : " + Title);
            
            _categories.Add("/state/com.google/read");
            RaisePropertyChanged(() => Unread);
        }

        public bool MarkAsStarred()
        {
            if (Starred)
                return false;        

            _categories.Add("/state/com.google/starred");

            RaisePropertyChanged(() => Starred);

            return true;
        }

        public bool CanMarkAsUnread()
        {
            if (Starred || Unread)
                return false;

            if (IsReadStateLocked)
                return false;

            return true;
        }
        public void MarkAsUnread()
        {
            //Debug.WriteLine("MarkAsUnread : " + Title);

            if (!CanMarkAsUnread())
                return;

            _categories.RemoveAll(s => s.EndsWith("/state/com.google/read"));

            RaisePropertyChanged(() => Unread);

        }

        public bool UnmarkAsStarred()
        {
            if (!Starred)
                return false;

            // A starred item may be unread, but only long as it's starred
            if(Unread)
                _categories.Add("/state/com.google/read");            

            _categories.RemoveAll(s => s.EndsWith("/state/com.google/starred"));

            RaisePropertyChanged(() => Starred);

            return true;
        }

        private void CleanTitle()
        {
            if (string.IsNullOrWhiteSpace(Title))
                return;

            Title = HtmlTagCleaner.ProcessAllTags(WebUtility.HtmlDecode(Title));
        }

        internal void Initialize()
        {
            // Finding picture
            if (DisplaySummary == null)
            {
                _preparedDescription = Strings.NoSummary;
                return;
            }
            var data = DisplaySummary.Data ?? string.Empty;
            var regex = new Regex(@"(?<img>\<img.+?\>)");
            var matches = regex.Matches(data);

            if (matches.Count > 0)
            {
                var matchPictures = new List<MatchPicture>();

                foreach (Match match in matches)
                {
                    if (match.Success)
                    {
                        var picture = new MatchPicture
                                          {
                                              DetectedWidth = 0
                                          };

                        var imgData = match.Groups["img"].Value;

                        regex = new Regex(@"src=\""(?<src>.+?)\""");
                        var miscMatch = regex.Match(imgData);
                        picture.Url = miscMatch.Groups["src"].Value;


                        regex = new Regex(@"width=\""(?<width>\d+?)\""");
                        miscMatch = regex.Match(imgData);

                        if (miscMatch.Success)
                        {
                            picture.DetectedWidth = int.Parse(miscMatch.Groups["width"].Value);
                        }

                        data = data.Replace(match.Value, "");

                        // Exclude tracking images
                        if(picture.DetectedWidth != 1)
                            matchPictures.Add(picture);
                    }
                }

                ImageUrl =
                    matchPictures.Where(m => m.DetectedWidth == 0 || m.DetectedWidth >= 64).OrderByDescending(m => m.DetectedWidth).Select(m => m.Url).
                        FirstOrDefault();
            }
            _preparedDescription = data;

            EnsureDescriptionInitialized();

            CleanTitle();

            // If the item is marked as starred & Unread, mark it as read
            // as this is a quirk and is not really supported by GReader
            if (Starred && Unread)
            {
                _categories.Add("/state/com.google/read");
                IsReadStateLocked = true; // Prevent further changes to this until refresh, etc.
            } 
        }

        private void EnsureDescriptionInitialized()
        {
            if (string.IsNullOrEmpty(_description))
            {
                var data = _preparedDescription;

                if (data == null)
                    return;

                if (data.Length > 1000)
                {
                    var pos = 300; // data.IndexOf("\n", 1000);
                    data = data.Substring(0, pos >= 0 ? pos : 1000);
                }

                data = data.Replace("\n", "");

                data = HtmlTagCleaner.ProcessTag("p", data, "\n");
                data = HtmlTagCleaner.ProcessTag("div", data, "\n");
                data = HtmlTagCleaner.ProcessTag("span", data, "\n");
                data = HtmlTagCleaner.ProcessTag("h1", data, "\n");
                data = HtmlTagCleaner.ProcessTag("h2", data, "\n");
                data = HtmlTagCleaner.ProcessTag("h3", data, "\n");
                data = HtmlTagCleaner.ProcessUnaryTag("br", data, "\n");

                data = HtmlTagCleaner.ProcessAllTags(data);
                data = HtmlTagCleaner.ProcessAllUnaryTags(data);

                _description = WebUtility.HtmlDecode(data.Trim());

            }
        }

        public static bool operator ==(Item left, Item right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(Item left, Item right)
        {
            return !Equals(left, right);
        }
    }
}